package edu.uci.ics.ai.maze.views;

import edu.uci.ics.ai.maze.models.GameModel;
import edu.uci.ics.ai.maze.models.Polygon;
import edu.uci.ics.ai.maze.utils.Constants;
import edu.uci.ics.ai.maze.models.Vertex;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JPanel;

/**
 * Draws the canvas containing the vertices and polygons.
 */
public class MazeCanvas extends JPanel implements Observer {

  private final GameModel model;
  private final VisibleVertexDialogModel visibleVertexModel;

  public MazeCanvas(GameModel theModel, VisibleVertexDialogModel visibleVertexModel) {
    super();
    this.model = theModel;
    this.visibleVertexModel = visibleVertexModel;

    theModel.addObserver(this);
    visibleVertexModel.addObserver(this);

    setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
    setBackground(Color.white);
    setSize(0, 0);
  }

  @Override
  public void update(Observable obj, Object arg) {
    repaint();
  }

  @Override
  public void paint(Graphics g) {
    super.paint(g);
    drawXYcoordinate(g);
    if (model.getMaze() != null) {
      drawMaze(g);
    }
    drawPath(g);
    if (visibleVertexModel.getVisibleVertices() != null) {
      drawVisibleVert(g);
    }
  }

  public void drawXYcoordinate(Graphics g) {
    g.setColor(Color.black);
    g.drawLine(Constants.xOfset, Constants.yOfset,
        Constants.xOfset + Constants.MAZE_HORIZONTAL, Constants.yOfset);
    g.drawLine(Constants.xOfset + Constants.MAZE_HORIZONTAL, Constants.yOfset,
        Constants.xOfset + Constants.MAZE_HORIZONTAL,
        Constants.yOfset + Constants.MAZE_VERTICAL);
    g.drawLine(Constants.xOfset + Constants.MAZE_HORIZONTAL,
        Constants.yOfset + Constants.MAZE_VERTICAL,
        Constants.xOfset, Constants.yOfset + Constants.MAZE_VERTICAL);
    g.drawLine(Constants.xOfset, Constants.yOfset + Constants.MAZE_VERTICAL,
        Constants.xOfset, Constants.yOfset);
    model.setScaleFactor(1 + Math.max(Constants.enlarge * model.getMaze().getMaxX()
        / Constants.MAZE_HORIZONTAL, Constants.enlarge * model.getMaze().getMaxY()
        / Constants.MAZE_VERTICAL));

    Font fnt = new Font("Arial", Font.PLAIN, 8);
    g.setFont(fnt);

    for (int i = model.getScaleFactor();
        i < model.getScaleFactor() * Constants.MAZE_HORIZONTAL / Constants.enlarge;
        i += model.getScaleFactor()) {
      int x_coord = (i / model.getScaleFactor()) * Constants.enlarge;
      g.setColor(Color.lightGray);
      g.drawLine(x_coord + Constants.xOfset, Constants.yOfset + Constants.MAZE_VERTICAL,
          x_coord + Constants.xOfset, Constants.yOfset);

      if (i % (model.getScaleFactor() * 5) == 0) {
        g.setColor(Color.black);
        g.drawLine(x_coord + Constants.xOfset, Constants.yOfset + Constants.MAZE_VERTICAL,
            x_coord + Constants.xOfset, Constants.yOfset + Constants.MAZE_VERTICAL - 5);
        g.drawString(String.valueOf(i),
            x_coord + Constants.xOfset - 2, Constants.yOfset + Constants.MAZE_VERTICAL + 10);
      }
    }

    int j = 0;
    for (int i = model.getScaleFactor() * Constants.MAZE_VERTICAL / Constants.enlarge; i >= 0;
        i -= model.getScaleFactor(), j += model.getScaleFactor()) {
      int y_coord = (i / model.getScaleFactor()) * Constants.enlarge;
      g.setColor(Color.lightGray);
      g.drawLine(Constants.xOfset, Constants.yOfset + y_coord,
          Constants.xOfset + Constants.MAZE_HORIZONTAL, Constants.yOfset + y_coord);

      if (j % (model.getScaleFactor() * 5) == 0) {
        g.setColor(Color.black);
        g.drawLine(Constants.xOfset, Constants.yOfset + y_coord,
            Constants.xOfset + 5, Constants.yOfset + y_coord);
        g.drawString(String.valueOf(j),
            Constants.xOfset - 20, Constants.yOfset + y_coord + 4);
      }
    }

  }

  public void drawPath(Graphics g) {
    g.setColor(Color.black);
    Pen p = new Pen(g);
    for (int i = 1; i < model.getSearchResult().getPointsInPath(); i++) {
      p.drawLine(Constants.xOfset
          + Constants.enlarge * model.getSearchResult().getPath().get(i - 1).getX()
          / model.getScaleFactor(),
          Constants.yOfset + Constants.MAZE_VERTICAL
          - Constants.enlarge * model.getSearchResult().getPath().get(i - 1).getY()
          / model.getScaleFactor(),
          Constants.xOfset
          + Constants.enlarge * model.getSearchResult().getPath().get(i).getX()
          / model.getScaleFactor(),
          Constants.yOfset + Constants.MAZE_VERTICAL - Constants.enlarge
          * model.getSearchResult().getPath().get(i).getY() / model.getScaleFactor());
    }
  }

  public void drawVisibleVert(Graphics g) {

    if (visibleVertexModel.getCurVertex() != null) {
      g.setXORMode(Color.green);
      drawVertex(visibleVertexModel.getCurVertex(), g, Color.MAGENTA);
      g.setColor(Color.yellow);
      Pen p = new Pen(g);
      if (visibleVertexModel.getSelVisVert() == -1) {
        for (int i = 0; i < visibleVertexModel.getNumVisVert(); i++) {
          p.drawLine(Constants.xOfset + Constants.enlarge * visibleVertexModel.getCurVertex().getX()
              / model.getScaleFactor(),
              Constants.yOfset + Constants.MAZE_VERTICAL
              - Constants.enlarge * visibleVertexModel.getCurVertex().getY() / model.getScaleFactor(),
              Constants.xOfset + Constants.enlarge * visibleVertexModel.getVisibleVertices()[i].getX()
              / model.getScaleFactor(),
              Constants.yOfset + Constants.MAZE_VERTICAL - Constants.enlarge
              * visibleVertexModel.getVisibleVertices()[i].getY() / model.getScaleFactor());
        }
      } else {
        p.drawLine(Constants.xOfset + Constants.enlarge * visibleVertexModel.getCurVertex().getX()
            / model.getScaleFactor(),
            Constants.yOfset + Constants.MAZE_VERTICAL - Constants.enlarge * visibleVertexModel.getCurVertex().getY()
            / model.getScaleFactor(),
            Constants.xOfset + Constants.enlarge * visibleVertexModel.getVisibleVertices()[visibleVertexModel.getSelVisVert()].getX()
            / model.getScaleFactor(),
            Constants.yOfset + Constants.MAZE_VERTICAL - Constants.enlarge
            * visibleVertexModel.getVisibleVertices()[visibleVertexModel.getSelVisVert()].getY() / model.getScaleFactor());
      }
    }
  }

  public void drawMaze(Graphics g) {
    if (model.getMaze().getStart() != null) {
      drawVertex(model.getMaze().getStart(), g, Color.RED);
    }
    if (model.getMaze().getGoal() != null) {
      drawVertex(model.getMaze().getGoal(), g, Color.BLUE);
    }

    for (Polygon p : model.getMaze().getPolygons()) {
      drawPolygon(p, g);
    }
  }

  public void drawPolygon(Polygon polygon, Graphics g) {
    int x, y;
    g.setColor(polygon.getColor());

    if (polygon.getEdgesCount() == 2) {
      g.drawLine(Constants.enlarge * polygon.getVertices().get(0).getX() / model.getScaleFactor()
          + Constants.xOfset,
          Constants.MAZE_VERTICAL - (Constants.enlarge * polygon.getVertices().get(0).getY()
          / model.getScaleFactor()) + Constants.yOfset,
          Constants.enlarge * polygon.getVertices().get(1).getX() / model.getScaleFactor()
          + Constants.xOfset,
          Constants.MAZE_VERTICAL - (Constants.enlarge * polygon.getVertices().get(1).getY()
          / model.getScaleFactor()) + Constants.yOfset);
    } else {
      java.awt.Polygon awtPolygon = new java.awt.Polygon();
      for (int i = 0; i < polygon.getEdgesCount(); i++) {
        x = Constants.enlarge * polygon.getVertices().get(i).getX()
            / model.getScaleFactor() + Constants.xOfset;
        y = Constants.MAZE_VERTICAL - (Constants.enlarge * polygon.getVertices().get(i).getY()
            / model.getScaleFactor()) + Constants.yOfset;
        awtPolygon.addPoint(x, y);
      }
      g.fillPolygon(awtPolygon);
    }
  }

  public void drawVertex(Vertex v, Graphics g, Color color) {
    g.setColor(color);

    int x_coordinate = (Constants.xOfset + Constants.enlarge * v.getX()
        / model.getScaleFactor() - Constants.pointRad);
    int y_coordinate = (Constants.MAZE_VERTICAL - Constants.enlarge * v.getY()
        / model.getScaleFactor() - Constants.pointRad + Constants.yOfset);
    g.fillOval(x_coordinate, y_coordinate,
        2 * Constants.pointRad, 2 * Constants.pointRad);
  }
}
